{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "dad6d72aacc5d152",
   "metadata": {},
   "source": [
    "# <ins>Evaluation of Generator Reactive Power Capability Characteristics in Load Flow Analysis</ins>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a48551a371b704d9",
   "metadata": {},
   "source": [
    "By incorporating the reactive power capability characteristics of a generator, the minimum **(Qmin)** and maximum **(Qmax)** reactive power limits can be determined as a function of the generator’s active power output. This directly influences the load flow result, which will be demonstrated in subsequent sections of this tutorial.\n",
    "\n",
    "The **q_capability_curve_table** provides a structured representation of the generator’s reactive power limits and consists of the following key variables:\n",
    "  * **id_q_capability_curve** – Identifier for the reactive power capability curve in the **q_capability_curve_table**.  <br>\n",
    "  * **min_q_mvar** – Minimum reactive power limit (Qmin). <br>\n",
    "  * **max_q_mvar** – Maximum reactive power limit (Qmax). <br>\n",
    "\n",
    "To facilitate the integration of these limits, the function **create_q_capability_characteristics_object** is employed. This function transforms the provided reactive power curve data into a characteristic object, applying linear interpolation to construct a continuous function. The resulting characteristic is stored in the **q_capability_characteristic** data structure.\n",
    "\n",
    "Following this, a diagnostic function is executed to identify and report any inconsistencies within the dataset, ensuring data integrity."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1f1536c2f0a9ab1a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "from pandapower.control.util.auxiliary import create_q_capability_characteristics_object, plot_characteristic\n",
    "from pandapower.control.util.diagnostic import q_capability_curve_table_diagnostic\n",
    "from pandapower.run import runpp\n",
    "from pandapower.create import (\n",
    "    create_empty_network,\n",
    "    create_bus,\n",
    "    create_ext_grid,\n",
    "    create_line_from_parameters,\n",
    "    create_load,\n",
    "    create_transformer_from_parameters,\n",
    "    create_gen\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "59203e36b3b7505a",
   "metadata": {},
   "outputs": [],
   "source": [
    "#Create network\n",
    "def test_net_for_q_capability_curve(characteristics_data=False):\n",
    "    net = create_empty_network()\n",
    "    bus1 = create_bus(net, name=\"bus1\", vn_kv=20., type=\"b\", min_vm_pu=0., max_vm_pu=1.05)\n",
    "    bus2 = create_bus(net, name=\"bus2\", vn_kv=110., type=\"b\", min_vm_pu=0., max_vm_pu=1.05)\n",
    "    bus3 = create_bus(net, name=\"bus3\", vn_kv=110., type=\"b\", min_vm_pu=0., max_vm_pu=1.05)\n",
    "\n",
    "    create_ext_grid(net, bus3, name=\"External Grid\", vm_pu=1.0, va_degree=0.0,max_p_mw=100000, min_p_mw=0, min_q_mvar=-300, max_q_mvar=300,\n",
    "                       s_sc_max_mva=10000, s_sc_min_mva=8000, rx_max=0.1, rx_min=0.1)\n",
    "    # create lines\n",
    "    create_line_from_parameters(net, bus2, bus3, length_km=10,df=1,max_loading_percent=100,vn_kv=110,max_i_ka=0.74,type=\"ol\",\n",
    "                   r_ohm_per_km=0.0949, x_ohm_per_km =0.38, c_nf_per_km=0.0092, name=\"Line\")\n",
    "    # create load\n",
    "    create_load(net, bus3, p_mw=200, q_mvar=180, name=\"Load\", vm_pu=1.0 )\n",
    "\n",
    "    # create transformer\n",
    "    create_transformer_from_parameters(net, bus2, bus1, name=\"110kV/20kV transformer\", parallel=1,max_loading_percent=100,sn_mva=210,\n",
    "    vn_hv_kv=110, vn_lv_kv=20, vk_percent=12.5, vkr_percent=0.01904762, vk0_percent=10, vkr0_percent=0,\n",
    "                          shift_degree=330, vector_group=\"YNd11\", i0_percent= 0.26, pfe_kw=0,si0_hv_partial=0.5)\n",
    "\n",
    "    create_gen(net, bus1, p_mw=100, sn_mva=255.0, scaling=1.0, type=\"Hydro\",\n",
    "                 cos_phi=0.8, pg_percent=0.0, vn_kv=19.0, vm_pu=1.0)\n",
    "    if characteristics_data:\n",
    "        net[\"q_capability_curve_table\"] = pd.DataFrame(\n",
    "        {'id_q_capability_curve': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
    "        'p_mw':      [-331.01001, -298.0,    -198.0,     -66.2000,   -0.1,        0,           0.1,        66.200, 100,     198.00,     298.00,     331.01001],\n",
    "        'q_min_mvar':[-0.0100,    -134.0099, -265.01001, -323.01001, -323.01001,  -323.01001, -323.01001, -323.01001, 0, -265.01001, -134.00999, -0.01000 ],\n",
    "        'q_max_mvar':[0.01000,    134.00999,  228.00999,  257.01001,  261.01001,  261.01001,   261.01001,  257.01001, 30,  40,  134.0099,   0.01]})\n",
    "\n",
    "        net.gen.loc[0,\"id_q_capability_characteristic\"] = 0\n",
    "        net.gen['curve_style'] = \"straightLineYValues\"\n",
    "\n",
    "    return net"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b71ba05d2de786e5",
   "metadata": {},
   "source": [
    "#### <ins>No reactive power limit applied<ins>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ec02f98f1277a7b7",
   "metadata": {},
   "outputs": [],
   "source": [
    "net1 = test_net_for_q_capability_curve()\n",
    "runpp(net1)\n",
    "net1.gen.loc[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "569b6aacbb16caea",
   "metadata": {},
   "source": [
    "Upon examining the lookup table, it is observed that no values were initially assigned to the variables **min_q_mvar** and **max_q_mvar**. These values are now assigned, thereby completing the definition of the generator’s reactive power characteristic."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7b2c09d00209c628",
   "metadata": {},
   "source": [
    "#### <ins>General reactive power limits enforced</ins>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "62951e84cbf71ae5",
   "metadata": {},
   "outputs": [],
   "source": [
    "net2 = test_net_for_q_capability_curve()\n",
    "net2.gen.loc[0,\"max_q_mvar\"] = 50.0\n",
    "net2.gen.loc[0, \"min_q_mvar\"] =  -3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c3c93660c2f1940e",
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net2, enforce_q_lims=True)\n",
    "assert net2.res_gen.q_mvar.loc[0] == -3\n",
    "assert net2.res_gen.p_mw.loc[0] == 100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dbaccadc497fb13b",
   "metadata": {},
   "outputs": [],
   "source": [
    "net2.gen.loc[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "380503001984e644",
   "metadata": {},
   "source": [
    "#### <ins>Create Q characteristics table</ins>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8be14b6573be06b5",
   "metadata": {},
   "outputs": [],
   "source": [
    "net3 = test_net_for_q_capability_curve(characteristics_data=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f34779a057f2d046",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Add q_capability_characteristic for one gen based on q_capability_curve_table\n",
    "create_q_capability_characteristics_object(net3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28a99e583397566c",
   "metadata": {},
   "source": [
    "The generator's characteristic has now been added to the dataframe. Refer to the lookup table **q_capability_curve_table** below for details:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8a6d07a145b5c885",
   "metadata": {},
   "outputs": [],
   "source": [
    "net3.q_capability_curve_table"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b9eb5bb6f9a45426",
   "metadata": {},
   "source": [
    "After executing the **create_q_capability_characteristics_object** function, the following column in the **net.gen** are updated:  **reactive_capability_curve**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5d66e2385aee9b42",
   "metadata": {},
   "outputs": [],
   "source": [
    "net3.gen.loc[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2621394f057e1309",
   "metadata": {},
   "source": [
    "The **q_capability_characteristic** table is look like below:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1bfa18e7589bfc10",
   "metadata": {},
   "outputs": [],
   "source": [
    "net3.q_capability_characteristic"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5364f895a1caf626",
   "metadata": {},
   "source": [
    "The characteristic function created within this process is a **Characteristic** object, utilizing SciPy’s **interp1d** for **linear interpolation**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "68aeb29c6786da09",
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_characteristic(net3.q_capability_characteristic.q_max_characteristic.at[0], -331, 331,\n",
    "                               xlabel='Values of \"P_mw\"', ylabel='Value of \"Q_max_Mvar\"')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fce6270e5893444a",
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_characteristic(net3.q_capability_characteristic.q_min_characteristic.at[0], -331, 331,\n",
    "                               xlabel='Values of \"P_mw\"', ylabel='Value of \"Q_min_Mvar\"')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "37921ef85ac1abe4",
   "metadata": {},
   "outputs": [],
   "source": [
    "runpp(net3, enforce_q_lims=True)\n",
    "assert net3.res_gen.q_mvar.loc[0] == 0\n",
    "assert net3.res_gen.p_mw.loc[0] == 100"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "60f5654844423431",
   "metadata": {},
   "source": [
    "# Validation and Impact on Load Flow\n",
    "To verify the impact of the adjusted values on the power system simulation:\n",
    "* Voltage magnitudes at generator buses are examined. <br>\n",
    "* Reactive power generation is observed to remain constant when the generator reaches its defined limits during load flow analysis.\n",
    "* A comparative analysis is performed to evaluate the difference in results across three cases:\n",
    "    * No reactive power limit applied <br>\n",
    "    * General reactive power limits enforced<br>\n",
    "    * Limits derived from the characteristic table"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "55f8fd006d6b97ce",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"\\n --------No reactive power limit applied-----------------\\n\")\n",
    "print(f\"Qmin={net1.gen.min_q_mvar.loc[0]}\")\n",
    "print(f\"QMax={net1.gen.max_q_mvar.loc[0]}\\n\")\n",
    "print(net1.res_gen)\n",
    "print(net1.res_bus)\n",
    "print(\"\\n --------General reactive power limits enforced-------------\\n\")\n",
    "print(f\"Qmin={net2.gen.min_q_mvar.loc[0]}\")\n",
    "print(f\"QMax={net2.gen.max_q_mvar.loc[0]}\\n\")\n",
    "print(net2.res_gen)\n",
    "print(net2.res_bus)\n",
    "print(\"\\n --------Limits derived from the characteristic table---------\\n\")\n",
    "print(f\"Qmin={net3.gen.min_q_mvar.loc[0]}\")\n",
    "print(f\"QMax={net3.gen.max_q_mvar.loc[0]}\\n\")\n",
    "print(net3.res_gen)\n",
    "print(net3.res_bus)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b43dd0330bb0d46b",
   "metadata": {},
   "source": [
    "The results confirm that applying the reactive power capability characteristics leads to distinct voltage profiles and a controlled reactive power response, significantly influencing the overall power system behavior."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fadf01f27e6f351",
   "metadata": {},
   "source": [
    "# Diagnostic\n",
    "The\n",
    "diagnostic\n",
    "function\n",
    "for generator reactive power characteristics checks the following:\n",
    "\n",
    "* Are there Q capability curve characteristic table?\n",
    "* Are there ** reactive_capability_curve **, ** curve_style ** and ** id_q_capability_characteristic ** missing in net.gen or net.sgen?\n",
    "*Check if all\n",
    "relevant\n",
    "columns\n",
    "for creating Q capability characteristics are populated in the ** q_capability_curve_table **\n",
    "*Check\n",
    "the\n",
    "data\n",
    "types\n",
    "of ** reactive_capability_curve ** and ** id_q_capability_characteristic **\n",
    "* Are there any missing ** id_q_capability_characteristic ** values in the ** q_capability_curve_table ** ?\n",
    "\n",
    "The\n",
    "results\n",
    "are\n",
    "displayed\n",
    "to\n",
    "the\n",
    "user\n",
    "with the help of warning statements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eff36b043eb6ec97",
   "metadata": {},
   "outputs": [],
   "source": [
    "net4 = test_net_for_q_capability_curve(characteristics_data=True)\n",
    "create_q_capability_characteristics_object(net4)\n",
    "q_capability_curve_table_diagnostic(net4, \"gen\")\n",
    "#runpp(net4, enforce_q_lims=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e54df313fe91e0d0",
   "metadata": {},
   "outputs": [],
   "source": [
    "net4.gen.loc[0,\"reactive_capability_curve\"] = False\n",
    "q_capability_curve_table_diagnostic(net4, \"gen\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3d308b2d09f0921a",
   "metadata": {},
   "outputs": [],
   "source": [
    "net4 = test_net_for_q_capability_curve(characteristics_data=True)\n",
    "create_q_capability_characteristics_object(net4)\n",
    "net4.gen.loc[0,\"reactive_capability_curve\"] = False\n",
    "net4.gen.loc[0,\"id_q_capability_characteristic\"] = np.nan\n",
    "q_capability_curve_table_diagnostic(net4, \"gen\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1ae372be738e8927",
   "metadata": {},
   "outputs": [],
   "source": [
    "net4 = test_net_for_q_capability_curve(characteristics_data=True)\n",
    "create_q_capability_characteristics_object(net4)\n",
    "net4.gen.loc[0, \"curve_style\"] = \"hi\"\n",
    "net4.q_capability_curve_table.drop(columns=\"q_min_mvar\")\n",
    "q_capability_curve_table_diagnostic(net4, \"gen\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "65b9ff6ec2ccedd7",
   "metadata": {},
   "outputs": [],
   "source": [
    "net4 = test_net_for_q_capability_curve(characteristics_data=True)\n",
    "create_q_capability_characteristics_object(net4)\n",
    "net4.q_capability_curve_table.q_min_mvar = np.nan\n",
    "net4.q_capability_curve_table.drop(columns=\"q_min_mvar\")\n",
    "q_capability_curve_table_diagnostic(net4, \"gen\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {},
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
